﻿using System;
using System.Xml;
using System.Reflection;
using System.IO;
using System.Collections.Generic;

namespace Ruandao.Setting
{
    using Ruandao.Misc;

    /// <summary>
    /// 从xml文件中读取设置的初始值。
    /// xml文件的约定：
    /// 1. 文件定位方式：主目录、或者主目录下的名为Bin或App_Data子目录。其中，
    ///     1）对于exe应用程序：主目录是指当前应用程序域的BaseDirectory，一般为启动exe文件所在位置。
    ///     2）对于web应用程序：主目录是指～所映射到的目录。
    /// 2. 文件名是:settings.xml，
    /// xml文件中定义了各个OptionData的初始数据，支持的数据类型有：基本类型（包括可空泛型）、枚举、基本类型的集合和字典。
    /// </summary>
    /// 用单例模式来实现，同一个WebApplication仅需要一个。
    public class InitialSettings
    {
        public readonly static InitialSettings Instance = new InitialSettings();

        private Dictionary<string, OptionData> options = new Dictionary<string,OptionData>();

        private InitialSettings()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(LocateSettingFile());
            
            foreach (XmlNode subNode in doc.DocumentElement.ChildNodes)
            {
                if (subNode.LocalName != "option") continue;
                string name = subNode.Attributes["name"].Value;
                
                OptionData option = new OptionData(name);

                foreach (XmlNode propertyNode in subNode.SelectNodes("property"))
                {
                    SetProperty(typeof(OptionData), option, propertyNode.Attributes["name"].Value, propertyNode.InnerText);
                }
                options.Add(name, option);

            }
        }
         
        /// <summary>
        /// 按设置项的名称检索初始设置值。 如果在配置文件中没有指定初始值，那么使用构造方法中硬编码的初始值。
        /// </summary>
        public OptionData GetInitialValue(string name)
        {
            return options.ContainsKey(name) ? options[name] : new OptionData(name);
        }

        /// <summary>
        /// 定位Setting设置文件的位置。
        /// <br/>异常：InvalidCodeException(没有找到文件)
        /// </summary>
        /// <returns>映射文件的绝对路径。</returns>
        private string LocateSettingFile()
        {
            string xmlfile = "settings.xml";
            string path;
            string pathfile = "";

            path = AppDomain.CurrentDomain.BaseDirectory;

            pathfile = Path.Combine(path, xmlfile);
            if (File.Exists(pathfile))
            {
                return pathfile;
            }

            pathfile = Path.Combine(path, "Bin\\" + xmlfile);
            if (File.Exists(pathfile))
            {
                return pathfile;
            }

            pathfile = Path.Combine(path, "App_Data\\" + xmlfile);
            if (File.Exists(pathfile))
            {
                return pathfile;
            }

            throw new InvalidCodeException("无法定位" + xmlfile + "文件，其位置约定是应用程序域的主目录、或者主目录下的名为Bin或App_Data子目录。");
        }

        private static void SetProperty(Type optionType, Object optionData, string property, string valueString)
        {
            PropertyInfo propertyInfo = optionType.GetProperty(property);
            Type type = propertyInfo.PropertyType; 
            object value = TypeUtil.ConvertTo(valueString, type);
            propertyInfo.SetValue(optionData, value, null);
        }
    }
}

